/**********************************************************************************************/
/* FBL_DebugNew.cpp																			  */
/*                                                                       					  */
/* Copyright Paradigma, 1993-2006                                        					  */
/* All Rights Reserved.                                                 					  */
/**********************************************************************************************/
// Idea of DebugNew module is based on the Metrowerks CodeWarrior DebugNew file.
// We have greatly extend and improve this feature.
// It works now also on Windows and with MS Visual C++.
// This implemenation is special features for Valentina Test System.
//
#include <VShared/FBL/publ/Headers/StdAfx.h>

#if DEBUG_NEW != 0

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif //_MSC_VER

#define _FBL_Exceptions_h
#include <VShared/FBL/publ/Utilities/FBL_Util_PathA.h>

#undef new

#include <new>

#include <fstream>

#if FBL_MAC_MACHO
#include <sys/malloc.h>
#else
#include <malloc.h>
#endif

#if FBL_UNIX
#define ulong FBL::ulong
#endif //FBL_UNIX

#pragma onlySTD_keywords off
#pragma ANSI_strict off

#include <VShared/FBL/publ/TestTools/FBL_DebugNew.h>
#include <stdlib.h>


/**********************************************************************************************/
#define NEWMODE_SIMPLE	1			//	call NewPtr/DisposPtr
#define NEWMODE_MALLOC	2			//	use malloc/free
#define NEWMODE_NORMAL	3			//	use New.cp new/delete               | Set NEWMODE in
#define NEWMODE_FAST	4			//	use New.cp new/delete fast version  | New.cp to same!

#ifndef NEWMODE
	#if TARGET_RT_MAC_CFM && TARGET_CPU_68K
		#define NEWMODE NEWMODE_FAST		//  workaround for CFM68K shared lib runtimes
	#else
		#define NEWMODE NEWMODE_MALLOC		//	mode used to compile this file
	#endif
#endif


#if DEBUG_NEW >= DEBUG_NEW_BASIC


/**********************************************************************************************/
// is used by Debug New to give more exact info about Leak location.
FBL_Begin_Namespace

#if FBL_STATIC || SKW_VX
	extern char* gLatestTestFunction;
#else
	char* gLatestTestFunction;
#endif //FBL_STATIC;

FBL_End_Namespace

FBL_Using_Namespace


/**********************************************************************************************/
bool gDebugNewWatchLeaks = true;


/**********************************************************************************************/
static void 			DefaultErrorHandler(short);
static void 			ZapBlock(void* ptr, register STD::size_t len, register long value);
static BlockStatus_t 	GetBlockStatus(char* ptr, bool fValidateFree);
static void 			ReportBlockStatus(BlockStatus_t status);
static BlockHeader* 	GetHeader(void* ptr);
static void*			FinishAllocate(char* ptr, STD::size_t size, const char* file, int line, const char* func, bool is_array);


/**********************************************************************************************/
// global data used by DebugNew
	
unsigned long 						gDebugNewAllocCount; // these three variables are public
unsigned long						gDebugNewAllocCurr;  // total #bytes currently allocated
unsigned long						gDebugNewAllocMax;	 // max #bytes ever allocated

unsigned long						gDebugNewTotalCount; // RZ
unsigned long						gDebugDeleteTotalCount; // RZ

static void* 						gHeapBegin;
static void* 						gHeapEnd;
static DebugNewErrorHandler_t		gErrorHandler = DefaultErrorHandler;

#if DEBUG_NEW == DEBUG_NEW_LEAKS

long DebugNewReportLeaks();

	// With leak checking enabled, we must store a list of all
	// allocated blocks so we can tell what has been leaked.
	// The block list is stored as a hash table, with the key
	// being the block address.

#pragma warn_implicitconv off
const int	kHashTableSize		=	499; // a prime number
static BlockHeader* 				gBlockHash[kHashTableSize];
static inline int Hash(const void* p) {return ((unsigned long)p>>3) % kHashTableSize;}

#pragma warn_implicitconv reset
#endif


unsigned long gDebugNewFlags = dnCheckBlocksInApplZone;


/**********************************************************************************************/
#define	  BLOCK_HEADER_SIZE	(sizeof(BlockHeader))
#define	  BLOCK_TRAILER_SIZE (sizeof(BlockTrailer))
#define	  BLOCK_OVERHEAD	(BLOCK_HEADER_SIZE + BLOCK_TRAILER_SIZE)

// Block zapping values. Newly allocated and free blocks are filled with
// these values to detect uses of uninitialize memory and released memory.
// Different values are used so you can look at a piece of memory
// in the debugger and tell if it is allocated but uninitialized or
// if it was released.
#define	  ZAP_UNINITIALIZED	0xF1F1F1F1L
#define	  ZAP_RELEASED		0xF3F3F3F3L


// some random, hopefully not too common, odd values 
#define	  BLOCK_PREFIX_ALLOC		0xD1F3D1F3L
#define	  BLOCK_PREFIX_ALLOC_ARRAY	0xD1F5D1F5L		// Hsoi 980821 added
#define	  BLOCK_PREFIX_FREE			0xF7B9F7B9L
#define	  BLOCK_SUFFIX				0xB7D5B7D5L


/**********************************************************************************************/
#ifndef DEBUG_NEW_NO_GLOBAL_OPERATORS

typedef void* (*_op_new)(STD::size_t);  // hh 981221
typedef void* (*_op_new_nothrow)(STD::size_t, const STD::nothrow_t&);  // hh 981221

#if NEWMODE==NEWMODE_SIMPLE

	#include <pool_alloc.h>

	namespace
	{
		void* (*alloc_func)(STD::size_t) = __sys_alloc;
		void  (*free_func)(void*)         = __sys_free;
	}

#elif NEWMODE==NEWMODE_MALLOC

	namespace
	{
		void* (*alloc_func)(STD::size_t) = /*STD::*/malloc;
		void  (*free_func)(void*)         = /*STD::*/free;
	}

#elif NEWMODE==NEWMODE_NORMAL || NEWMODE==NEWMODE_FAST

	#ifndef _MSL_NO_CPP_NAMESPACE
		namespace std {
	#endif
		void* my_alloc(STD::size_t);
		void my_free(void*);
	#ifndef _MSL_NO_CPP_NAMESPACE
		}
	#endif

	namespace
	{
		void* (*alloc_func)(STD::size_t) = STD::my_alloc;
		void  (*free_func)(void*)         = STD::my_free;
	}

#endif


/**********************************************************************************************/
void* operator new(STD::size_t size) throw(STD::bad_alloc)
{
	return DebugNewDoAllocate(size, 0, 0, 0, alloc_func, false);  // hh 981221
}

// hh 981221
void *operator new(STD::size_t size, const STD::nothrow_t& nt) throw()
{
	return DebugNewDoAllocate(size, 0, 0, 0, alloc_func, nt, false);
}

// Hsoi 980821 added
void *operator new[](STD::size_t size) throw(STD::bad_alloc)
{
	return DebugNewDoAllocate(size, 0, 0, 0, alloc_func, true);  // hh 981221
}

// hh 981221
void *operator new[](STD::size_t size, const STD::nothrow_t& nt) throw()
{
	return DebugNewDoAllocate(size, 0, 0, 0, alloc_func, nt, true);
}

/**********************************************************************************************/
#if DEBUG_NEW == DEBUG_NEW_LEAKS

void* operator new(STD::size_t size, const char* file, int line, const char* func) throw(STD::bad_alloc)


{
	return DebugNewDoAllocate(size, file, line, func, alloc_func, false);  // hh 981221
}

// hh 981221
void* operator new(STD::size_t size, const STD::nothrow_t& nt, const char* file, int line, const char* func) throw()
{
	return DebugNewDoAllocate(size, file, line, func, alloc_func, nt, false);
}

// Hsoi 980821 added
void* operator new[](STD::size_t size, const char* file, int line, const char* func) throw(STD::bad_alloc)
{
	return DebugNewDoAllocate(size, file, line, func, alloc_func, true);  // hh 981221
}

// hh 981221
void* operator new[](STD::size_t size, const STD::nothrow_t& nt, const char* file, int line, const char* func) throw()
{
	return DebugNewDoAllocate(size, file, line, func, alloc_func, nt, true);
}

#endif // DEBUG_NEW == DEBUG_NEW_LEAKS


#pragma mark -


/**********************************************************************************************/
void operator delete(void* ptr) throw()
{
	DebugNewDoFree(ptr, free_func, false);
}

void operator delete(void* ptr, const STD::nothrow_t&) throw()
{
	operator delete(ptr);
}

void operator delete(void* ptr, const char*, int, const char*) throw()
{
	operator delete(ptr);
}

void operator delete(void* ptr, const STD::nothrow_t&, const char*, int, const char*) throw()
{
	operator delete(ptr);
}

// Hsoi 980821 added
void operator delete[](void* ptr) throw()
{
	DebugNewDoFree(ptr, free_func, true);
}

void operator delete[](void* ptr, const STD::nothrow_t&) throw()
{
	//operator delete[](ptr);
	DebugNewDoFree(ptr, free_func, false);
}

void operator delete[](void* ptr, const char*, int, const char*) throw()
{
	//operator delete[](ptr);
	DebugNewDoFree(ptr, free_func, false);
}

void operator delete[](void* ptr, const STD::nothrow_t&, const char*, int, const char*) throw()
{
	//operator delete[](ptr);
	DebugNewDoFree(ptr, free_func, false);
}

#endif	// !DEBUG_NEW_NO_GLOBAL_OPERATORS


#pragma mark -
#pragma mark ===== Callbacks for DLLs ======


/**********************************************************************************************/
void* fbl_new( STD::size_t size, bool is_array )
{
	return DebugNewDoAllocate(size, 0, 0, 0, alloc_func, is_array); 
}

void* fbl_new( STD::size_t size, const STD::nothrow_t&,  bool is_array )
{
	return DebugNewDoAllocate(size, 0, 0, 0, alloc_func, is_array); 
}

void fbl_delete(void* ptr) throw()
{
	DebugNewDoFree(ptr, free_func, false);
}

void fbl_delete_array(void* ptr) throw()
{
	DebugNewDoFree(ptr, free_func, true);
}

#pragma mark -


/**********************************************************************************************/
void DebugNewDoFree(void* ptr, void (*free_func)(void* ptr), bool is_array)
{
	// Hsoi 980821 added is_array argument and logic
	
		// Allocation counter should be > zero. If not, then delete was
		// called too many times.
		
		// JWW - ...except if the thing to delete is NULL
	if (!gDebugNewAllocCount && ptr)
	{
		gErrorHandler(dbgnewTooManyFrees);
		return;
	}
	
	BlockStatus_t status = GetBlockStatus((char*)ptr, false);
	switch (status)
	{
		case blockFree:
		case blockPtrOutOfRange:
		case blockBadHeader:
		case blockBadTrailer:
			ReportBlockStatus(status);
			return;
			
		case blockPtrNull:	 // it's legal to pass NULL to operator delete
			return;		
	}

	BlockHeader* h = GetHeader(ptr);
	if (!h)
	{
		ReportBlockStatus(blockBadHeader);
		return;
	}
	
	long goodTag = is_array ? (long)BLOCK_PREFIX_ALLOC_ARRAY : (long)BLOCK_PREFIX_ALLOC;
	if (h->tag != goodTag)
	{
		ReportBlockStatus(blockDeleteNotMatch);
		return;
	}

	const bool fDoFree = !(gDebugNewFlags & dnDontFreeBlocks);

#if DEBUG_NEW == DEBUG_NEW_LEAKS

		// find the block in the block list
		int hash = Hash(h);
		BlockHeader* curr = gBlockHash[hash];
		BlockHeader* prev = 0;
		while (curr)
		{
			if (curr == h)
				break;
			prev = curr;
			curr = curr->next;	
		}
		if (!curr)
		{
			gErrorHandler(dbgnewBlockNotInList);
			return;
		}
		else if (fDoFree)
		{
			// if this block was ignored by leak system then we increase counter
			// to compensate --gDebugNewAllocCount in the "if (fDoFree)" few lines down.
			if( curr->line == -1 ) 
				++gDebugNewAllocCount;
						
			if (prev)
				prev->next = curr->next;
			else
				gBlockHash[hash] = curr->next;
			curr->next = 0;
		}

#endif // DEBUG_NEW == DEBUG_NEW_LEAKS

	ZapBlock(ptr, h->size + BLOCK_TRAILER_SIZE, ZAP_RELEASED);
			
	// mark block freed, and free it
	h->tag = BLOCK_PREFIX_FREE;
	if (fDoFree)
	{
		++gDebugDeleteTotalCount;
		--gDebugNewAllocCount;
		
		gDebugNewAllocCurr -= h->size;
		free_func((char*)ptr - BLOCK_HEADER_SIZE);  
	}
}

void* DebugNewDoAllocate(STD::size_t size, const char* file, int line, const char* func,
		void* (*alloc_func)(STD::size_t size), bool is_array)	
{		
	// hh 000118 Rewrote to use malloc/free		
	char* p;
	while (true)
	{
		p = (char*)alloc_func(size+BLOCK_OVERHEAD);
		if (p != 0)
			break;
		STD::new_handler f = STD::set_new_handler(0);
		STD::set_new_handler(f);
		if (!f)
			throw STD::bad_alloc();
		f();
	}

	return FinishAllocate(p, size, file, line, func, is_array);		
}

// hh 981221
void* DebugNewDoAllocate(STD::size_t size, const char* file, int line, const char* func,
		void* (*alloc_func)(STD::size_t size), const STD::nothrow_t&, bool is_array)	
{		
	// hh 000118 Rewrote to use malloc/free		
	char* p;
	while (true)
	{
		p = (char*)alloc_func(size+BLOCK_OVERHEAD);
		if (p != 0)
			break;
		STD::new_handler f = STD::set_new_handler(0);
		STD::set_new_handler(f);
		if (!f)
			return 0;
		try
		{
			f();
		}
		catch (...)
		{
			return 0;
		}
	}

	return FinishAllocate(p, size, file, line, func, is_array);
}

// Hsoi 990122 added - created to consolidate common code in both
// versions of DebugNewDoAllocate
static void* FinishAllocate(char* p, STD::size_t size, const char* file, int line, const char* func, bool is_array)
{
#if DEBUG_NEW < DEBUG_NEW_LEAKS
#pragma unused(file, line, func)
#endif // DEBUG_NEW < DEBUG_NEW_LEAKS

	if( gDebugNewWatchLeaks )	
	{
		++gDebugNewTotalCount;
		++gDebugNewAllocCount;		
	}
	
	gDebugNewAllocCurr += size;		
	if (gDebugNewAllocCurr > gDebugNewAllocMax)
		gDebugNewAllocMax = gDebugNewAllocCurr;

		// zap data area + trailer, so blocks of sizes that are
		// not multiples of four get fully zapped.
		
	ZapBlock(p+BLOCK_HEADER_SIZE, size+BLOCK_TRAILER_SIZE, ZAP_UNINITIALIZED);
	
	BlockHeader* h = (BlockHeader*)p;
		h->size = size;
	h->tag = is_array ? (long)BLOCK_PREFIX_ALLOC_ARRAY : (long)BLOCK_PREFIX_ALLOC;
	h->padSize = 0; // assume no padding. No way to tell this if there
					// is compiler-inserted padding at this stage anyways.
	
	BlockTrailer* t = (BlockTrailer*)(p + size + BLOCK_HEADER_SIZE);
	t->tag = BLOCK_SUFFIX;
		
#if DEBUG_NEW == DEBUG_NEW_LEAKS

		// new blocks go to front of the block list
	int hash = Hash(h);
	h->next = gBlockHash[hash];
	gBlockHash[hash] = h;
	h->file = file;

	// we will mark line only if we watch for leaks. 
	// Otherwise leak system will ignore this block.
	if( gDebugNewWatchLeaks )	
		h->line = line;
	else
		h->line = -1;

	h->func=func;
	h->test_func = gLatestTestFunction;
	h->counter = (int) gDebugNewTotalCount;

#if 0 // These is really useless - so I remove it from compilation.
	if( file )
	{
		const char* pc = ExtractNameFromFullPath( file );
		if( strcmp(pc, "fbl_memptr.h" ) == 0 && h->size == 4 )
		{
			int i = 1;
			i;
		}
	}
#endif //0

#endif // DEBUG_NEW == DEBUG_NEW_LEAKS
	
	return p + BLOCK_HEADER_SIZE;
}


static BlockStatus_t GetBlockStatus(char* ptr, bool fValidateFree)
{
	if (!ptr)
		return blockPtrNull;

// JWW - This check isn't good under OS X, so it's completely taken out for Carbon
// It'd probably be better to test the system version Gestalt value around this
// instead of completely ignoring the test under Carbon, but that would make
// execution time slower.
//#if !TARGET_API_MAC_CARBON
//	if (gDebugNewFlags & dnCheckBlocksInApplZone)
//	{
//		// Validate that pointer is in the application heap. This
//		// will also trap attempts to free pointers whose values
//		// are inside uninitialized or free blocks, i.e.
//		// 0xF1F1F1F1 or 0xF3F3F3F3.
//		THz applZone = LMGetApplZone();
//		if ((void*)ptr < &applZone->heapData || ptr > applZone->bkLim)
//			return blockPtrOutOfRange;
//	}
//#endif
	
	const BlockHeader& h = *GetHeader(ptr);
	if (!(&h))
	{
		return blockBadHeader;
	}
	
	// check block header
			
	if (h.tag == BLOCK_PREFIX_FREE)
	{
		if (fValidateFree)
		{
				// Check that free block contains the zap values, if not
				// then it was probably overwritten through a dangling
				// pointer
				
			const unsigned long* p = (const unsigned long*) ((char*)&h + BLOCK_HEADER_SIZE);
			long count = (long)((h.size+BLOCK_TRAILER_SIZE) / 4);
			while (--count >= 0)
			{
				if (*p++ != ZAP_RELEASED)
					return blockFreeOverwritten;
			}
		}
		return blockFree;
	}
		
	if ((h.tag != BLOCK_PREFIX_ALLOC) && (h.tag != BLOCK_PREFIX_ALLOC_ARRAY))
		return blockBadHeader;
		
	const BlockTrailer& t = *(BlockTrailer*) (ptr + (h.size - h.padSize));

	// check block trailer
	
	if (t.tag != BLOCK_SUFFIX)
		return blockBadTrailer;
		
	return blockValid;
}

static void ReportBlockStatus(BlockStatus_t status)
{
	switch (status)
	{
		case blockFree:
			gErrorHandler(dbgnewFreeBlock);
			break;
			
		case blockPtrNull:
			gErrorHandler(dbgnewNullPtr);
			break;
		
		case blockPtrOutOfRange:
			gErrorHandler(dbgnewPointerOutsideHeap);
			break;
			
		case blockBadHeader:
			gErrorHandler(dbgnewBadHeader);
			break;
		
		case blockBadTrailer:
			gErrorHandler(dbgnewBadTrailer);
			break;
			
		case blockNotInList:
			gErrorHandler(dbgnewBlockNotInList);
			break;
			
		case blockFreeOverwritten:
			gErrorHandler(dbgnewFreeBlockOverwritten);
			break;
		
		case blockDeleteNotMatch:
			gErrorHandler(dbgnewMismatchedNewDelete);
			break;
		
		default:
			gErrorHandler(-1);
			break;
	}
}

DebugNewErrorHandler_t DebugNewSetErrorHandler(DebugNewErrorHandler_t newHandler)
{
	DebugNewErrorHandler_t oldHandler = gErrorHandler;
	if (newHandler)
		gErrorHandler = newHandler;
	return oldHandler;
}

static void DefaultErrorHandler(short err)
{
	char* s;
	switch (err)
	{
		case dbgnewNullPtr:
			s = (char*)"DebugNew: null pointer";
			break;
			
		case dbgnewTooManyFrees:
			s = (char*)"DebugNew: more deletes than news";
			break;
			
		case dbgnewPointerOutsideHeap:
			s = (char*)"DebugNew: delete or validate called for pointer outside application heap";
			break;
			
		case dbgnewFreeBlock:
			s = (char*)"DebugNew: delete or validate called for free block";
			break;
			
		case dbgnewBadHeader:
			s = (char*)"DebugNew: unknown block, block header was overwritten, or mismatched new and delete[]";
			break;
			
		case dbgnewBadTrailer:
			s = (char*)"DebugNew: block trailer was overwritten";
			break;
			
		case dbgnewBlockNotInList:
			s = (char*)"DebugNew: block valid but not in block list (internal error)";
			break;
			
		case dbgnewFreeBlockOverwritten:
			s = (char*)"DebugNew: free block overwritten, could be dangling pointer";
			break;
									
		case dbgnewMismatchedNewDelete:
			s = (char*)"DebugNew: mismatched regular/array new and delete (probably delete on new[] block)";
			break;
								
		default:
			s = (char*)"DebugNew: undefined error";
			break;
	}

	FBL_CHECK( (s, false) );
//	LogError( ERR_EXTERNAL_ERROR, 0, s, 0 );
}


static void ZapBlock(void* ptr, register STD::size_t len, register long value)
{
	register long* p = (long*) ptr;
	len /= sizeof(long);
	while (len-- > 0)
		*p++ = value;
}

#if DEBUG_NEW == DEBUG_NEW_LEAKS

void  DebugNewValidateAllBlocks()
{
	// traverse the block list and validate every block
	for (int i = 0; i < kHashTableSize; i++)
	{
		BlockHeader* curr = gBlockHash[i];
		while (curr)
		{
			BlockStatus_t status = GetBlockStatus((char*)curr + BLOCK_HEADER_SIZE, true);
			if (status != blockValid && status != blockFree)
			{
				ReportBlockStatus(status);
				// abort on any error. Not strictly necessary for some errors,
				// but we could crash if next field is trashed.
				break;
			}		
			curr = curr->next;
		}
	}
}


/**********************************************************************************************/
int CompareHeaders( BlockHeader* a, BlockHeader* b );
int CompareHeaders( BlockHeader* a, BlockHeader* b )
{
	return a->counter < b->counter;
}


/**********************************************************************************************/
void BuildSortedArrayOfLeaks( int inLeaksCount, BlockHeader**& outArray, ulong inCounter )
{
	BlockHeader** array = (BlockHeader**) malloc( sizeof(BlockHeader*) * inLeaksCount );
	BlockHeader** p = array;

	BlockHeader* curr;
	for (int i = 0; i < kHashTableSize; i++)
	{
		curr = gBlockHash[i];
		while (curr)
		{
			if( (curr->tag == BLOCK_PREFIX_ALLOC || curr->tag == BLOCK_PREFIX_ALLOC_ARRAY) 
				&& curr->line >= 0 && curr->counter > inCounter )
			{
				*p++ = curr;
			}
			curr = curr->next;
		}
	}

	// now we need do quicksort.	
	std::sort( array, array + inLeaksCount, CompareHeaders );
	
	outArray = array;
}


/**********************************************************************************************/
void FreeSortedArrayOfLeaks( BlockHeader** inArray )
{
	free(inArray);
}


/**********************************************************************************************/
void DebugNewWatchLeaksStart( void )
{
	gDebugNewWatchLeaks = true;
}


/**********************************************************************************************/
void DebugNewWatchLeaksStop( void )
{
	gDebugNewWatchLeaks = false;
}


/**********************************************************************************************/
ulong CountLeaks( ulong& count, ulong& totalAlloc, ulong& bytesLeaked, ulong inCounter )
{
	BlockHeader* curr;

	ulong leakCount = 0;

	for (int i = 0; i < kHashTableSize; i++)
	{
		curr = gBlockHash[i];
		while (curr)
		{
			totalAlloc += curr->size; // count freed blocks, since gDebugNewAllocCurr does

			++count;
			if ((curr->tag == BLOCK_PREFIX_ALLOC || curr->tag == BLOCK_PREFIX_ALLOC_ARRAY) 
				&& curr->line >= 0 && curr->counter > inCounter )
			{
				bytesLeaked += curr->size;
				++leakCount;
			}
			curr = curr->next;
		}
	}
	
	return leakCount;
}


/**********************************************************************************************/
long DebugNewReportLeaks()
{
	std::ofstream file;
	file.open( "FBL_Leaks.txt", std::ios_base::out );
	std::ofstream& leaksFile = file;
	
	BlockHeader* curr;

	ulong count = 0;
	ulong bytesLeaked = 0;
	ulong totalAlloc = 0;

	ulong leakCount = CountLeaks( count, totalAlloc, bytesLeaked );
	
	BlockHeader** array;
	BuildSortedArrayOfLeaks( (int) leakCount, array );
	
	// DO REPORTING:
	if(count != gDebugNewAllocCount)
		leaksFile << "Warning: length of block list different from count of allocated blocks (internal error).\n";
		
	leaksFile << "Maximum #bytes allocated at any point via operator new: " << gDebugNewAllocMax << "\n";
	
	if (!leakCount)
	{
		leaksFile << "No memory leaks.\n";
		leaksFile.flush();
		leaksFile.close();
		return 0;
	}

	if (leakCount == 1)
		leaksFile << "There is 1 memory leak of bytes:" << bytesLeaked << "\n";
	else
		leaksFile << "There are " << leakCount << " memory leaks, totaling bytes:" << bytesLeaked << "\n";
	
	for( long i = 0; i < leakCount ; ++i )
	{
		curr = array[i];

		if ((curr->tag == BLOCK_PREFIX_ALLOC || curr->tag == BLOCK_PREFIX_ALLOC_ARRAY) && curr->line >= 0)
		{
			if (curr->file)
			{
				const char* fileName = ExtractNameFromFullPath( curr->file );
				const char* pFunc = curr->func ? curr->func : " <unknown> ";
				const char* pTest = curr->test_func ? curr->test_func : " <unknown> ";

				leaksFile 	<< "	" 		<< fileName 
							<< ": func: " 	<< pFunc 
							<< ": line: " 	<< (ulong)curr->line 
							<< ", size: " 	<< (ulong) curr->size;
				leaksFile 	<< ", test: " 	<< pTest; 
			}
			else
				leaksFile << "  <unknown>, size: " << (ulong)curr->size;
			
			// Hsoi 980821 - compiler-inserted padding is part of BlockHeader.size and
			// will show up in the leaks.log (above). To help people compensate when
			// reviewing the log, we'll notify them of any compiler-inserted padding
			// (see comments in GetHeader for more information). If you see the ptr
			// has padding, subtract that value from the reported size to get the true
			// pointer/data size.
			
			if (curr->padSize > 0)
				leaksFile << " (compiler-inserted padding: " << (ulong)curr->padSize;
			
			leaksFile << "\n";
		}
	} 
		
	if (totalAlloc != gDebugNewAllocCurr)
		leaksFile << "Warning: total allocations in block list different from gDebugNewAllocCurr.\n";

	leaksFile << "\nEND OF LEAKS LIST";
	leaksFile.flush();
	leaksFile.close();
	
	free( array );
		
	return (long) leakCount;
}

void DebugNewForgetLeaks()
{
	for (int i = 0; i < kHashTableSize; i++)
	{
		BlockHeader* curr = gBlockHash[i];
		while (curr)
		{
			if (curr->tag == BLOCK_PREFIX_ALLOC || curr->tag == BLOCK_PREFIX_ALLOC_ARRAY)
			{
				curr->file = NULL;
				curr->line = -1;
			}
			curr = curr->next;
		}
	}
}

#else
	// leak checking disabled, does nothing
long DebugNewReportLeaks()
{
	return 0;
}
void DebugNewForgetLeaks()
{
}
#endif	// DEBUG_NEW_LEAKS

#endif  //  DEBUG_NEW >= DEBUG_NEW_BASIC

void DebugNewValidatePtr(void* ptr)
{
#if DEBUG_NEW == DEBUG_NEW_OFF
#pragma unused(ptr)
#endif

#if DEBUG_NEW >= DEBUG_NEW_BASIC
	BlockStatus_t status = GetBlockStatus((char*)ptr, true);
#if DEBUG_NEW == DEBUG_NEW_LEAKS
	if (status == blockValid)
	{
		const BlockHeader* h = GetHeader(ptr);
		if (!h)
		{
			ReportBlockStatus(blockBadHeader);
			return;
		}

		// find the block in the block list
		int hash = Hash(h);
		BlockHeader* curr = gBlockHash[hash];
		while (curr)
		{
			if (curr == h)
				break;
			curr = curr->next;	
		}
		if (!curr)
			status = blockNotInList;
	}
#endif
	if (status != blockValid)
		ReportBlockStatus(status);
#endif
}

long DebugNewGetPtrSize(void* ptr)
{
#if DEBUG_NEW == DEBUG_NEW_OFF
#pragma unused(ptr)
#endif

	long size = -1;
	
#if DEBUG_NEW >= DEBUG_NEW_BASIC
	BlockStatus_t status = GetBlockStatus((char*)ptr, true);
	if (status == blockValid)
	{
		const BlockHeader* h = GetHeader(ptr);
		if (!h)
		{
			ReportBlockStatus(blockBadHeader);
			return size;
		}

		size = (long)(h->size - h->padSize);
	}
	else
		ReportBlockStatus(status);
#endif
	return size;
}

// Hsoi 980821 - added

#if DEBUG_NEW >= DEBUG_NEW_BASIC

static BlockHeader* GetHeader(void* ptr)
{
	// The header should be located just before the pointer
	BlockHeader* h = (BlockHeader*)((char*)ptr - BLOCK_HEADER_SIZE);
	
	if ((h->tag == BLOCK_PREFIX_ALLOC) ||
		(h->tag == BLOCK_PREFIX_ALLOC_ARRAY) ||
		(h->tag == BLOCK_PREFIX_FREE))
	{
		// We should be ok.
		h->padSize = 0;
		return h;
	}

	// If we get here, the header block might be corrupt, or there might
	// be something extra in the header (e.g. compiler inserted padding).
	// As of this writing (21 Aug 98), the Metrowerks C/C++ compilers
	// and runtimes (at least on MacOS) function this way. Any array
	// allocation where the array element type is a class with a non-trivial
	// constructor (e.g. classes with virtual members/bases or user
	// defined constructors) will allocate an additional 8-byte
	// (sizeof(STD::size_t)*2) header to store the size of a single
	// class object (in the first half of the header block) and the
	// total number of array elements (in the second half of the block).
	// See the Runtime Common source NMWException.cp, and functions like
	// __construct_new_array() and __destroy_new_array().
	//
	// NB: This compiler behavior may change future versions of the
	// compiler. For example, this header may only be allocated if
	// the class has a destructor. Furthermore, Win32/x86 compilers
	// may use a completely different strategy to be compatible with
	// the WinABI.
	//
	// To attempt to contend with this padding, we'll assume our header is
	// back there somewhere, so we'll walk backwards looking for
	// a tag. If we find it, correct h for this offset and return.
	// If do not find a known tag, either the header is corrupt
	// or the inserted padding is greater than BLOCK_HEADER_SIZE (unlikely
	// but always possible).
	//
	// This assumes ptr is a valid C++ new/new[] pointer, and that
	// there is a BlockHeader somewhere close before ptr.
	
	h = 0;
	
	char* bufferHead = (char*)ptr - BLOCK_HEADER_SIZE;
	STD::size_t pad = 0;
	for ( char* ref = (char*)ptr; ref >= bufferHead; --ref )
	{
		long refAsLong = *((long*)ref);
		
		if ((refAsLong == BLOCK_PREFIX_ALLOC) ||
			(refAsLong == BLOCK_PREFIX_ALLOC_ARRAY) ||
			(refAsLong == BLOCK_PREFIX_FREE))
		{
			// Found it. Adjust for the BlockHeader
			
			h = (BlockHeader*)(ref - (sizeof(BlockHeader) - sizeof(long)));
			h->padSize = pad - sizeof(long);
			return h;
		}
		
		pad++;
	}

	// problem			
	
	if ( h == 0 )
		gErrorHandler(dbgnewBadHeader);
	
	return h;
}

#endif // DEBUG_NEW >= DEBUG_NEW_BASIC

#pragma onlySTD_keywords reset
#pragma ANSI_strict reset

#endif
// hh   980126 added "using namespace std"
// Hsoi 980819 Fixed a bug in DebugNewReportLeaks that cause an incorrect number of leaks to be reported
// Hsoi 980819 Added support for array new and array delete
// Hsoi 980821 Created GetHeader, implemented throughout code
// hh   981221 Added new(nothrow)
// Hsoi 990122 Fixed FSSpecOpen to use HSetVol/HGetVol instead of SetVol/GetVol
// Hsoi 990122 Wrapped GetHeader with a check #if DEBUG_NEW >= DEBUG_NEW_BASIC
// Hsoi 990122 Added FinishAllocate() method to consolidate code from DebugNewDoAllocate overloads
// Hsoi 990316 Cleaned up implicit arithmetic conversion warnings.
// hh   000118 Rewrote to use malloc/free
// JWW	000202 Updated for Carbon compliance
// JWW	000217 Ignore the delete count on NULL deletions
// hh	000612 Defines NEWMODE to decide what allocator to use
// JWW  010116 Changed STD::size_t to STD::size_t
// JWW  010120 Took out general "using namespace std;" and added specific namespace scoping
// JWW  010327 Added a few missing STD:: qualifiers to some nothrow_t types
